using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Collections;
using System.Windows.Forms;
using System.Linq;
using System.IO;
using BesAsm.Framework.Tracer;
using BesAsm.MasterData.Data;
using BesAsm.MasterData.Entities;
using BesAsm.MasterData.Services;
using BesAsm.MasterData.Gis;
using BesAsm.Framework.ArcGisUtilities;
using BesAsm.Emgaats.Core;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Framework;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Carto;
using BesAsm.Framework.Reporting;

namespace BesAsm.Emgaats.TracerUI
{
  /// <summary>
  /// An Emgaats extension for tracing a link network. Creates the StartLinks, StopLinks and TraceLinks
  /// collections and provides a central location for other tracer components to share data
  /// </summary>  
  [ClassInterface(ClassInterfaceType.None)]
  public class TracerExtension : ESRI.ArcGIS.Desktop.AddIns.Extension, IEmgaatsExtension
  {

    private EmgaatsList<Link> _masterLinks;
    private EmgaatsList<Link> _startLinks;
    private EmgaatsList<Link> _stopLinks;
    private EmgaatsList<Link> _traceLinks;
    private TracerUserControl _tracerControl;

    private ArcGisUtils _arcUtils;

    private LayerLibrarian _librarian;
    private Mediator _mediator;

    /// <summary>
    /// Raised when one of extension's data sources changes
    /// </summary>
    public event EventHandler RebindRequired;

    public const string TRACE_DATA_GROUP_NAME = "Trace Data"; //TODO: This could be a config item

    private static TracerExtension _tracerExtension;

    public TracerExtension()
    {
      _tracerExtension = this;
    }

    internal static TracerExtension GetExtension()
    {
      if (_tracerExtension == null)
      {
        UID uid = new UIDClass();
        uid.Value = ThisAddIn.IDs.TracerExtension;
        ArcMap.Application.FindExtensionByCLSID(uid);
      }
      return _tracerExtension;
    }

    /// <summary>
    /// Provides access to the StartLinks collection maintained by this extension
    /// </summary>
    public EmgaatsList<Link> StartLinks
    {
      get { return _startLinks; }
    }

    /// <summary>
    /// Provides access to the StopLinks collection maintained by this extension
    /// </summary>
    public EmgaatsList<Link> StopLinks
    {
      get { return _stopLinks; }
    }

    /// <summary>
    /// Provides access to the MasterLinks collection
    /// </summary>
    public EmgaatsList<Link> MasterLinks
    {
      get
      {
        LoadMasterLinks();
        return _masterLinks;
      }
    }

    /// <summary>
    /// Provides access to the TraceLinks collection maintained by this extension
    /// </summary>
    public EmgaatsList<Link> TraceLinks
    {
      get { return _traceLinks; }
    }

    /// <summary>
    /// Regenerates the StartLinks collection and refreshes the ArcGIS FeatureClass
    /// used for displaying the collection
    /// </summary>
    /// <returns>A reference to the StartLinks collection</returns>
    internal EmgaatsList<Link> RefreshStartLinks()
    {
      return (RefreshStartLinks(RefreshLinksOption.Layer));
    }

    private EmgaatsLayer UpdateStartLinksLayer(IFeatureLayer fl)
    {
      EmgaatsLayer startLinksLayer;
      startLinksLayer = new EmgaatsLayer(LibraryItem.StartLinks, fl);
      _startLinks = new EmgaatsList<Link>(ArcMap.Application, startLinksLayer);

      _startLinks.AddRange(
        _masterLinks.GetSelectedEntities());

      return startLinksLayer;
    }

    internal EmgaatsList<Link> RefreshStartLinks(RefreshLinksOption refreshFrom)
    {
      LoadMasterLinks();
      UnbindLibrarian();

      EmgaatsLayer masterLinksLayer = _masterLinks.Layer;
      EmgaatsLayer startLinksLayer = null;

      if (refreshFrom == RefreshLinksOption.InternalList)
      {
        if (_startLinks != null)
        {
          if (_startLinks.Count > 0)
          {
            masterLinksLayer.SelectFeatures(_startLinks.GetOids().ToArray(), true);
            IFeatureLayer fl = masterLinksLayer.NewLayerFromSelection(
              _librarian.GetDataName(LibraryItem.StartLinks), TRACE_DATA_GROUP_NAME, ArcMap.Application);
            UpdateStartLinksLayer(fl);
            masterLinksLayer.Clear();
          }
          else
          {
            IFeatureLayer fl = masterLinksLayer.EmptyLayer(
              _librarian.GetDataName(LibraryItem.StartLinks), ArcMap.Application);
            UpdateStartLinksLayer(fl);
            masterLinksLayer.Clear();
          }
        }
      }
      else
      {
        if (_startLinks != null)
        {
          startLinksLayer = _startLinks.Layer;
          masterLinksLayer.SelectFeatures(startLinksLayer.GetAllOids(), false);
        }

        IFeatureLayer fl = masterLinksLayer.NewLayerFromSelection(
          _librarian.GetDataName(LibraryItem.StartLinks), TRACE_DATA_GROUP_NAME, ArcMap.Application);
        UpdateStartLinksLayer(fl);
        masterLinksLayer.Clear();
      }

      BindLibrarian();

      return _startLinks;
    }

    /// <summary>
    /// Regenerates the StopLinks collection and refreshes the ArcGIS FeatureClass
    /// used for displaying the collection
    /// </summary>
    /// <returns>A reference to the StopLinks collection</returns>
    internal ArcList<Link> RefreshStopLinks()
    {
      return RefreshStopLinks(RefreshLinksOption.Layer);
    }

    private EmgaatsLayer UpdateStopLinksLayer(IFeatureLayer fl)
    {
      EmgaatsLayer stopLinksLayer;
      stopLinksLayer = new EmgaatsLayer(LibraryItem.StopLinks, fl);
      _stopLinks = new EmgaatsList<Link>(ArcMap.Application, stopLinksLayer);

      _stopLinks.AddRange(
        _masterLinks.GetSelectedEntities());
      return stopLinksLayer;
    }

    internal ArcList<Link> RefreshStopLinks(RefreshLinksOption refreshFrom)
    {
      LoadMasterLinks();
      UnbindLibrarian();

      EmgaatsLayer masterLinksLayer = _masterLinks.Layer as EmgaatsLayer;
      EmgaatsLayer stopLinksLayer = null;

      if (refreshFrom == RefreshLinksOption.InternalList)
      {
        if (_stopLinks != null)
        {
          if (_stopLinks.Count > 0)
          {
            masterLinksLayer.SelectFeatures(_stopLinks.GetOids().ToArray(), true);
            IFeatureLayer fl = masterLinksLayer.NewLayerFromSelection(
              _librarian.GetDataName(LibraryItem.StopLinks), TRACE_DATA_GROUP_NAME, ArcMap.Application);
            UpdateStopLinksLayer(fl);
            masterLinksLayer.Clear();
          }
          else
          {
            IFeatureLayer fl = masterLinksLayer.EmptyLayer(
              _librarian.GetDataName(LibraryItem.StopLinks), ArcMap.Application);
            UpdateStopLinksLayer(fl);
            masterLinksLayer.Clear();
          }
        }
      }
      else
      {
        if (masterLinksLayer.SelectionSet.Count == 0)
        {
          throw new Exception("No master links selected");
        }
        if (_stopLinks != null)
        {
          stopLinksLayer = _stopLinks.Layer as EmgaatsLayer;
          masterLinksLayer.SelectFeatures(stopLinksLayer.GetAllOids(), false);
        }

        IFeatureLayer fl = masterLinksLayer.NewLayerFromSelection(
          _librarian.GetDataName(LibraryItem.StopLinks), TRACE_DATA_GROUP_NAME, ArcMap.Application);
        UpdateStopLinksLayer(fl);
        masterLinksLayer.Clear();
      }

      BindLibrarian();

      return _stopLinks;
    }

    /// <summary>
    /// Performs a network traversal of the MasterLinks collection using the StartLinks and StopLinks collections.    
    /// </summary>
    /// <returns>A reference to the traced links collection</returns>
    public ArcList<Link> Trace()
    {
      IMouseCursor waitCursor = new MouseCursorClass();
      waitCursor.SetCursor(2); // wait cursor = 2

      LoadMasterLinks();
      UnbindLibrarian();

      IList<Link> traceList = _masterLinks.Trace(_startLinks, _stopLinks);
      EmgaatsLayer masterLinksLayer = _masterLinks.Layer as EmgaatsLayer;

      masterLinksLayer.SelectFeatures(
        traceList.Select(p => p.Objectid).ToArray(), true);

      IFeatureLayer traceLayer = masterLinksLayer.NewLayerFromSelection(
        _librarian.LayerCatalog[LibraryItem.TraceLinks], TRACE_DATA_GROUP_NAME, ArcMap.Application);

      _traceLinks = new EmgaatsList<Link>(
        ArcMap.Application, new EmgaatsLayer(LibraryItem.TraceLinks, traceLayer));

      _traceLinks.AddRange(traceList.ToArray());

      masterLinksLayer.ClearSelection();

      BindLibrarian();

      return _traceLinks;
    }

    /// <summary>
    /// Checks if MasterLinks has been retrieved from the server
    /// and loads it if not
    /// </summary>
    private void LoadMasterLinks()
    {
      if (_masterLinks != null && _masterLinks.Count == 0)
      {
        LinkService ls = new LinkService();
        _masterLinks.AddRange(ls.GetAll());
      }
    }

    /// <summary>
    /// Returns an ArcUtils for performing common GIS tracer operations
    /// </summary>
    public ArcGisUtils ArcUtils
    {
      get { return this._arcUtils; }
    }

    /// <summary>
    /// Provides a means for this extension to perform custom startup
    /// </summary>
    /// <param name="initializationData"></param>
    protected override void OnStartup()
    {
      base.OnStartup();

      _mediator = Mediator.AttachToMediator(this);
      _librarian = LayerLibrarian.AttachToLibrarian(this);

      BindLibrarian();

      _arcUtils = new ArcGisUtils(ArcMap.Application);
      return;
    }
    
    /// <summary>
    /// Determines whether Extension UI should be enabled
    /// </summary>
    public bool IsUserInterfaceEnabled
    {
      get
      {
        return _masterLinks != null;
      }
    }

    /// <summary>
    /// Enable the UI
    /// </summary>
    /// <param name="enable">true to enable, false to disable</param>
    public void EnableUserInterface(bool enable)
    {
      if (_tracerControl != null)
        _tracerControl.Enabled = enable;
    }

    /// <summary>
    /// Handles the ArcGIS NewDocument event
    /// </summary>
    protected void OnNewDocument()
    {

    }

    /// <summary>
    /// Handles the ArcGIS OpenDocument event
    /// </summary>
    protected void OnOpenDocument()
    {
      if (_masterLinks != null)
        _masterLinks.Trace(_startLinks, _stopLinks);
    }

    /// <summary>
    /// Handles the LayerLibrarian CheckIn event
    /// </summary>
    /// <param name="sender">The object raising the CheckIn event</param>
    /// <param name="e">A CheckInCheckOutArgs object indicating which layer was checked in</param>
    protected void OnCheckIn(object sender, CheckInCheckOutArgs e)
    {
      EmgaatsLayer emgLayer = new EmgaatsLayer(e.LibraryItem, e.Layer);

      switch (emgLayer.LibraryItem)
      {
        case LibraryItem.MasterLinks:
          _masterLinks = new EmgaatsList<Link>(ArcMap.Application, emgLayer);
          EnableUserInterface(IsUserInterfaceEnabled);
          break;
        case LibraryItem.StartLinks:
          _startLinks = new EmgaatsList<Link>(ArcMap.Application, emgLayer);
          break;
        case LibraryItem.StopLinks:
          _stopLinks = new EmgaatsList<Link>(ArcMap.Application, emgLayer);
          break;
        case LibraryItem.TraceLinks:
          _traceLinks = new EmgaatsList<Link>(ArcMap.Application, emgLayer);
          break;
        default:
          return;
      }
      //if (State == esriExtensionState.esriESEnabled)
      OnRebindRequired(EventArgs.Empty);
    }

    /// <summary>
    /// Handles the LayerLibrarian CheckOut event
    /// </summary>
    /// <param name="sender">The object raising the CheckOut event</param>
    /// <param name="e">A CheckOutCheckOutArgs object indicating which layer was checked out</param>
    protected void OnCheckOut(object sender, CheckInCheckOutArgs e)
    {
      switch (e.LibraryItem)
      {
        case LibraryItem.MasterLinks:
          _masterLinks = null;
          EnableUserInterface(false);
          break;
        case LibraryItem.StartLinks:
          _startLinks = null;
          break;
        case LibraryItem.StopLinks:
          _stopLinks = null;
          break;
        case LibraryItem.TraceLinks:
          _traceLinks = null;
          break;
        default:
          return;
      }

      //if (State == esriExtensionState.esriESEnabled)
      OnRebindRequired(EventArgs.Empty);
    }

    /// <summary>
    /// Raised when one of the Tracer Extensions data sources (StartLinks,
    /// StopLinks, TraceLinks or MasterLinks) has been added or removed
    /// from the ArcGIS session.
    /// </summary>
    /// <param name="e">Default event arg, not used here</param>
    protected void OnRebindRequired(EventArgs e)
    {
      if (RebindRequired != null)
        RebindRequired(this, e);
    }

    /// <summary>
    /// Starts event-binding to the LayerLibrarian events
    /// </summary>
    private void BindLibrarian()
    {
      LayerLibrarian.CheckedIn += OnCheckIn;
      LayerLibrarian.CheckedOut += OnCheckOut;
      //if (State == esriExtensionState.esriESEnabled)
      OnRebindRequired(EventArgs.Empty);
    }

    /// <summary>
    /// Stops event-binding to the LayerLibrarian events
    /// </summary>
    private void UnbindLibrarian()
    {
      LayerLibrarian.CheckedIn -= OnCheckIn;
      LayerLibrarian.CheckedOut -= OnCheckOut;
    }

    public EmgaatsLayer TraceLayer
    {
      get
      {
        return _librarian[LibraryItem.TraceLinks];
      }
    }

    /// <summary>
    /// Is extension compatible with system version?
    /// </summary>
    public bool IsExtensionCompatibleWithSystem
    {
      get
      {
        return Properties.Settings.Default.ExtensionSystemVersion ==
          Mediator.SystemVersion;
      }
    }

    public string Caption
    {
      get
      {
        return "Tracer Tool";
      }
    }

    public void ActivateExtension()
    {
      UID uid = new UIDClass();
      uid.Value = ThisAddIn.IDs.TracerUserControl;
      IDockableWindow win = ArcMap.DockableWindowManager.GetDockableWindow(uid);
      win.Show(true);
    }

    public bool Enabled
    {
      get { return IsExtensionCompatibleWithSystem; }
    }

  }

  /// <summary>
  /// 
  /// </summary>
  public enum RefreshLinksOption
  {
    /// <summary>
    /// 
    /// </summary>
    InternalList,
    /// <summary>
    /// 
    /// </summary>
    Layer
  } // class TracerExtension

}
